package com.itjeffrey.autocode.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.itjeffrey.autocode.constant.SysConstant;
import com.itjeffrey.autocode.enums.ExceptionEnum;
import com.itjeffrey.autocode.exception.ACException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.format.number.NumberStyleFormatter;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

/**
 * 基础工具类，包含String、Math、Method等相关
 *
 * @From: Jeffrey
 * @Date: 2020/11/9
 */
@Slf4j
public class ACBaseUtil {

    /**
     * 私有化工具类 防止被实例化
     */
    private ACBaseUtil() {
    }

    /**
     * 获取当前方法和行号
     *
     * @return
     */
    public static String getLineInfo() {
        StackTraceElement ste = new Throwable().getStackTrace()[1];
        return ste.getFileName() + " -> " + ste.getLineNumber() + "行";
    }

    /***
     * 字符串格式化
     * 将字符串  我是{},你是{},他是{} ----> 转成 我是{0},你是{1},他是{2}
     * @param message
     * @param prefix
     * @param suffix
     * @return
     */
    public static String formatMessage(String message, char prefix, char suffix) {
        LinkedList<Character> chars = new LinkedList<>();
        int j = 0;
        for (int i = 0; i < message.length(); i++) {
            if (prefix == message.charAt(i) && suffix == message.charAt(i + 1)) {
                chars.add(prefix);
                chars.add(Character.forDigit(j, 10));
                j++;
            } else {
                chars.add(message.charAt(i));
            }
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < chars.size(); i++) {
            sb.append(chars.get(i));
        }
        return sb.toString();
    }

    /**
     * 动态不拦截指定url
     *
     * @param urls
     */
    public static void ignoreUrlAuthenication(String... urls) {
        FilterChainProxy obj = (FilterChainProxy) SpringContextHolder.getBean("springSecurityFilterChain");
        List<SecurityFilterChain> securityFilterChains = (List<SecurityFilterChain>) getProperty(obj, "filterChains");
        for (String url : urls) {
            securityFilterChains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher(url), new Filter[0]));
        }

    }

    /**
     * 从请求中获取ip
     *
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String remoteAddr = request.getRemoteAddr();
        String forwarded = request.getHeader("X-Forwarded-For");
        String realIp = request.getHeader("X-Real-IP");

        String ip = null;
        if (realIp == null) {
            if (forwarded == null) {
                ip = remoteAddr;
            } else {
                ip = remoteAddr + "/" + forwarded;
            }
        } else {
            if (realIp.equals(forwarded)) {
                ip = realIp;
            } else {
                ip = realIp + "/" + forwarded.replaceAll(", " + realIp, "");
            }
        }
        return ip;
    }

    private static Object getProperty(Object obj, String fieldName) {
        try {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据自定义对象获取该对象的简单的类名称
     *
     * @param object
     * @return
     */
    public static String getObjClass(Object object) {
        String className;
        if (object == null) {
            return null;
        }
        Class aClass = object.getClass();
        className = aClass.toString().substring(aClass.toString().lastIndexOf(".") + 1);
        return className;
    }

    /**
     * 获取JSONString
     *
     * @param object
     * @return
     */
    public static String getJsonString(Object object) {
        if (object instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) object;
            return jsonArray.toJSONString();
        }
        if (object instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) object;
            return jsonObject.toJSONString();
        }
        return null;
    }

    /**
     * 获取JSONObject,如果object不是JSONObject则返回null
     *
     * @param object
     * @return
     */
    public static JSONObject getJsonObj(Object object) throws ACException {
        if (object instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) object;
            return jsonObject;
        } else {
            throw new ACException(ExceptionEnum.JSON_ANALYSE_ERROR);
        }
    }

    /**
     * 随机获取中文全名
     *
     * @return
     */
    public static String getChineseName() {
        Random random = new Random(System.currentTimeMillis());
        int index = random.nextInt(SysConstant.BAI_JIA_XING.length - 1);
        //获取姓
        String name = SysConstant.BAI_JIA_XING[index];
        //随机获取一个或两个名
        if (random.nextBoolean()) {
            name += getChinese();
        } else {
            name += getChinese() + getChinese();
        }
        return name;
    }

    /**
     * 获取中文名
     *
     * @return
     */
    public static String getChinese() {
        String str = null;
        int highPos, lowPos;
        Random random = new Random();
        highPos = (176 + Math.abs(random.nextInt(71)));//区码，0xA0打头，从第16区开始，即0xB0=11*16=176,16~55一级汉字，56~87二级汉字
        random = new Random();
        lowPos = 161 + Math.abs(random.nextInt(94));//位码，0xA0打头，范围第1~94列

        byte[] bArr = new byte[2];
        bArr[0] = (new Integer(highPos)).byteValue();
        bArr[1] = (new Integer(lowPos)).byteValue();
        try {
            str = new String(bArr, "GB2312");    //区位码组合成汉字
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 生成指定总位数left,指定小数位数right的浮点数
     *
     * @param left
     * @param right
     * @return
     */
    public static String generateFloat(int left, int right) {
        String zhengshu = "";
        String xiaoshu = "";
        Random random = new Random();
        for (int j = 0; j < right; j++) {
            xiaoshu = xiaoshu + random.nextInt(10);
        }
        for (int k = 0; k < (left - right); k++) {
            int i;
            do {
                i = random.nextInt(10);
            } while (i == 0);
            zhengshu = zhengshu + i;
        }
        return zhengshu + "." + xiaoshu;
    }

    /**
     * 获取随机ip
     *
     * @return
     */
    public static String getRandomIp() {
        //ip范围
        int[][] range = {{607649792, 608174079},//36.56.0.0-36.63.255.255
                {1038614528, 1039007743},//61.232.0.0-61.237.255.255
                {1783627776, 1784676351},//106.80.0.0-106.95.255.255
                {2035023872, 2035154943},//121.76.0.0-121.77.255.255
                {2078801920, 2079064063},//123.232.0.0-123.235.255.255
                {-1950089216, -1948778497},//139.196.0.0-139.215.255.255
                {-1425539072, -1425014785},//171.8.0.0-171.15.255.255
                {-1236271104, -1235419137},//182.80.0.0-182.92.255.255
                {-770113536, -768606209},//210.25.0.0-210.47.255.255
                {-569376768, -564133889}, //222.16.0.0-222.95.255.255
        };

        Random rdint = new Random();
        int index = rdint.nextInt(10);
        String ip = num2ip(range[index][0] + new Random().nextInt(range[index][1] - range[index][0]));
        return ip;
    }

    public static String num2ip(int ip) {
        int[] b = new int[4];
        String x;

        b[0] = ((ip >> 24) & 0xff);
        b[1] = ((ip >> 16) & 0xff);
        b[2] = ((ip >> 8) & 0xff);
        b[3] = (ip & 0xff);
        x = Integer.valueOf(b[0]) + "." + Integer.valueOf(b[1]) + "." + Integer.valueOf(b[2]) + "." + Integer.valueOf(b[3]);
        return x;
    }

    public static String base = "abcdefghijklmnopqrstuvwxyz0123456789";

    private static final String[] email_suffix = (
            "@gmail.com,@yahoo.com,@msn.com,@hotmail.com,@aol.com,@ask.com,@live" +
            ".com,@qq.com,@0355.net,@163.com,@163.net,@263.net,@3721.net,@yeah.net,@googlemail.com,@126.com,@sina" +
            ".com,@sohu.com,@yahoo.com.cn").split(",");

    public static int getNum(int start, int end) {
        return (int) (Math.random() * (end - start + 1) + start);
    }

    /**
     * 返回Email
     *
     * @param lMin 最小长度
     * @param lMax 最大长度
     * @return
     */
    public static String getEmail(int lMin, int lMax) {
        int length = getNum(lMin, lMax);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            int number = (int) (Math.random() * base.length());
            sb.append(base.charAt(number));
        }
        sb.append(email_suffix[(int) (Math.random() * email_suffix.length)]);
        return sb.toString();
    }

    /**
     * 数组转为流
     *
     * @param arr
     * @param <T>
     * @return
     */
    public static <T> Stream<T> arrToStream(T[] arr) {
        return ArrayUtils.isEmpty(arr) ? Stream.empty() : Arrays.stream(arr);
    }

    /**
     * @description: 随机生成单词
     * a生成单词个数；b生成单词最长长度
     */
    public static String[] getWords(int a, int b) {
        String[] words = {};
        if (a > 0 && b > 0) {
            words = new String[a];
            String[] red = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
                    "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"};
            for (int i = 0; i < a; i++) {
                String word = "";
                //随记单词的长度
                int randb = new Random().nextInt(b);
                for (int j = 0; j <= randb; j++) {
                    int randa = new Random().nextInt(26);
                    //随记单词的一个字母
                    if (j == 0) {
                        word = red[randa];
                    } else {
                        word += red[randa];
                    }
                }
                words[i] = word;
            }
        }
        return words;
    }

    /**
     * 删除空格、换行、制表符等
     * @param str
     * @return
     */
    public static String removeBlank(String str){
        String descStr;
        if(str == null){
            return str;
        }
        Pattern pattern = Pattern.compile("\\s*|\\t|\\r|\\n");
        Matcher matcher = pattern.matcher(str);
        descStr = matcher.replaceAll("");
        return descStr;
    }

    /**
     * 数字 整数部分 千分位
     * 注意：Number有丢失精度的情况
     *
     * @param number 数字
     * @param scale  小数位（0：数字实际小数位）
     */
    public static String convertToThousandBit(Number number, int scale) {
        Objects.requireNonNull(number, "number not be null");
        if (scale < 0) {
            throw new RuntimeException("scale require >= 0");
        }
        String pattern = "#,###";
        String numberStr = number instanceof BigDecimal ? ((BigDecimal) number).toPlainString() : number.toString();
        if (scale == 0 && numberStr.contains(".")) {
            scale = numberStr.length() - numberStr.indexOf(".") - 1;
        }
        if (scale > 0) {
            StringBuilder scaleZero = new StringBuilder();
            for (int i = 0; i < scale; i++) {
                scaleZero.append("0");
            }
            pattern += "." + scaleZero;
        }
        NumberFormat numberFormat = new NumberStyleFormatter(pattern).getNumberFormat(Locale.CHINA);
        return numberFormat.format(number).startsWith(".") ? "0" + numberFormat.format(number) : numberFormat.format(number);
    }

    /**
     * 获取计数器+1后的值
     * 1.原计数器字符串不允许为空，且仅包含数值
     * 2.计数器数值+1后的字符串不允许超过原计数器字符串长度
     * eg: 001 -> 002
     *     999 -> IllegalArgumentException
     *     93g -> IllegalArgumentException
     *     " " -> IllegalArgumentException
     * @param source
     * @return
     */
    public static String counter(String source){
        //check
        Assert.hasText(source, "source has not text");
        Pattern pattern = Pattern.compile("^[0-9]+$");
        Matcher matcher = pattern.matcher(source);
        boolean matches = matcher.matches();
        Assert.isTrue(matches, "source is invalid counter string");
        //parse source
        int length = source.length();
        Integer integer = Integer.valueOf(source);
        int length1 = integer.toString().length();
        //convert (+1)
        int newNum = integer + 1;
        StringBuilder sb = new StringBuilder();
        //length >= length1
        if(length - length1 > 0){
            int len = length - length1;
            for (int i = 0; i < len; i++) {
                sb.append("0");
            }
            sb.append(newNum);
        }else {
            Assert.isTrue(String.valueOf(newNum).length() <= length, "counter is exceeding the max!");
            sb.append(newNum);
        }
        //return
        return sb.toString();
    }

    /**
     * 判断Cron表达式是否有效
     * @param cronExpression
     * @return
     */
    public static boolean isValidCron(final String cronExpression){
        if(SysConstant.EVERY_SECS_CRON_EXPRESSION.equals(cronExpression)){
            return true;
        }
        CronTriggerImpl trigger = new CronTriggerImpl();
        try {
            trigger.setCronExpression(cronExpression);
            Date date = trigger.computeFirstFireTime(null);
            return date != null && date.after(new Date());
        } catch (Exception e) {
            log.error("[ACBaseUtil.isValidCron]:failed. throw e:" , e);
        }
        return false;
    }

}
